﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace Darwin.Source.Particle_Systems
{
    class CExplosion : CBaseParticleSystem
    {
        Effect m_ePointSpriteEffect;
        Texture2D m_t2DExplosionTexture;
        Random m_rRand;
        Vector3 m_v3Velocity;
        Vector3 m_v3VelocityVariation;
        float m_fSizeLimit;
        bool m_bFirstRun;

        float m_fWidth, m_fDepth, m_fHeight;

        public CExplosion(int maxParticles)
            : base(maxParticles)
        {
        }

        public override bool Initialise(Game game, float minHeight)
        {
            base.BaseInitialise(game, minHeight);
            m_vdVertexPosColDecl = new VertexDeclaration(r_gGame.GraphicsDevice, VertexPositionColor.VertexElements);

            for (int i = 0; i < m_vertData.Length; i++)
            {
                m_vertData[i].Position = m_ParticleArray[i].m_v3Position;
                m_vertData[i].Color = Color.Red;
            }

            m_rRand = new Random();
            m_iEmitRate = 8;
            m_fWidth = m_fDepth = 3.0f;
            m_fHeight = 3.0f;
            m_bFirstRun = true;

            // velocity
            m_v3Velocity = new Vector3(2.0f, -3.0f, -3.0f);
            m_v3VelocityVariation = new Vector3(-2.3f, 2.0f, 6.0f);

            m_fSizeLimit = 40.0f;

            return true;
        }

        public override void LoadContent()
        {
            // Load texture after base init
            try
            {
                m_t2DExplosionTexture = r_gGame.Content.Load<Texture2D>("GameData/Images/splode");
                m_ePointSpriteEffect = r_gGame.Content.Load<Effect>("GameData/Shaders/PointSprite");
                
            }
            catch (ContentLoadException e)
            {
                Console.WriteLine(e.Message);
            }
        }

        public override void EmitParticles(int numParticles)
        {
            while (numParticles != 0 && (m_iNumParticles < m_iMaxParticles))
            {
                // Set position in sky
                m_ParticleArray[m_iNumParticles].m_v3Position.X = m_v3EmitOrigin.X + (m_rRand.Next((int)m_fWidth) - m_rRand.Next((int)m_fWidth));
                m_ParticleArray[m_iNumParticles].m_v3Position.Y = m_v3EmitOrigin.Y + m_fHeight;
                m_ParticleArray[m_iNumParticles].m_v3Position.Z = m_v3EmitOrigin.Z + (m_rRand.Next((int)m_fDepth) - m_rRand.Next((int)m_fDepth));

                m_ParticleArray[m_iNumParticles].m_fSize = (float)(m_rRand.NextDouble() * 5.0f);
                m_ParticleArray[m_iNumParticles].m_fSizeDelta = 1.0f;

                // Update draw vert data
                m_vertData[m_iNumParticles].Position = m_ParticleArray[m_iNumParticles].m_v3Position;

                // Give random velocity
                m_ParticleArray[m_iNumParticles].m_v3Velocity.X = m_v3Velocity.X + (float)(m_rRand.NextDouble() * m_v3VelocityVariation.X);
                m_ParticleArray[m_iNumParticles].m_v3Velocity.Y = m_v3Velocity.Y + (float)(m_rRand.NextDouble() * m_v3VelocityVariation.Y);
                m_ParticleArray[m_iNumParticles].m_v3Velocity.Z = m_v3Velocity.Z + (float)(m_rRand.NextDouble() * m_v3VelocityVariation.Z);

                m_iNumParticles++;
                --numParticles;
            }
        }

        public override void DoUpdate(float elapsedTime)
        {
            if (m_bFirstRun)
            {
                EmitParticles(m_iMaxParticles);
                m_bFirstRun = false;
            }

            for (int i = 0; i < m_iNumParticles; ++i)
            {
                m_ParticleArray[i].m_v3Position += (m_ParticleArray[i].m_v3Velocity * elapsedTime);
                m_vertData[i].Position = m_ParticleArray[i].m_v3Position;
                m_ParticleArray[i].m_fSize += m_ParticleArray[i].m_fSizeDelta;
            }

            // Please dont fear the reaper
            Reaper();

            m_fAccumulatedTime += elapsedTime;

            if (m_iNumParticles == 0)
            {
                m_bFirstRun = true;
                m_bIsActive = false;
                return;
            }
        }

        public override void DoRender(Matrix projMatrix, Matrix viewMatrix)
        {
            // Set shader shizz
            r_gGame.GraphicsDevice.RenderState.PointSpriteEnable = true;
            
            r_gGame.GraphicsDevice.RenderState.AlphaBlendEnable = true;
            r_gGame.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
            //r_gGame.GraphicsDevice.RenderState.DestinationBlend = Blend.One;
            r_gGame.GraphicsDevice.RenderState.DepthBufferWriteEnable = false;

            r_gGame.GraphicsDevice.VertexDeclaration = m_vdVertexPosColDecl;
            Matrix WVPMatrix = Matrix.Identity * viewMatrix * projMatrix;
            m_ePointSpriteEffect.Parameters["WVPMatrix"].SetValue(WVPMatrix);

            m_ePointSpriteEffect.Parameters["SpriteTexture"].SetValue(m_t2DExplosionTexture);

            int current = 0;
            m_ePointSpriteEffect.Begin();
            foreach (EffectPass pass in m_ePointSpriteEffect.CurrentTechnique.Passes)
            {
                r_gGame.GraphicsDevice.RenderState.PointSize = m_ParticleArray[current].m_fSize;

                pass.Begin();
                r_gGame.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.PointList, m_vertData, 0, m_ParticleArray.Length);
                pass.End();

                ++current;
            }
            m_ePointSpriteEffect.End();

            r_gGame.GraphicsDevice.RenderState.PointSpriteEnable = false;
            r_gGame.GraphicsDevice.RenderState.DepthBufferWriteEnable = true;
            r_gGame.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
            r_gGame.GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
            r_gGame.GraphicsDevice.RenderState.DepthBufferWriteEnable = true;
        }

        public override void Reaper()
        {
            for (int i = 0; i < m_iNumParticles; )
            {
                if (m_ParticleArray[i].m_fSize >= m_fSizeLimit)
                {
                    CParticle dead = m_ParticleArray[i];
                    m_ParticleArray[i] = m_ParticleArray[--m_iNumParticles];
                    m_ParticleArray[m_iNumParticles] = dead;
                }
                else
                    ++i;
            }
        }

        protected override void InitialiseParticle(int index)
        {
        }

    }
}
